#include "new_kernels.cu"

#include <thrust/device_vector.h>
#include <thrust/device_ptr.h>

#include <thrust/transform.h>

#include <thrust/functional.h>
#include <thrust/count.h>
#include <thrust/sort.h>
#include <thrust/unique.h>

#include "stdio.h"
#include <cutil_inline.h>  
#define DEBUG
#ifdef DEBUG
#define MY_SAFE_CALL(x) cutilSafeCall(x)
#else
#define MY_SAFE_CALL(x) x
#endif


//----------------------------------------
// host code for KERNEL 1
//----------------------------------------

void callInitLabels(unsigned int* Labels,int widthImage,int heightImage,int threadsX,int threadsY)
{

  printf("starting initialisation function \n");
	dim3 block(threadsX, threadsY, 1);
	dim3 grid(widthImage / block.x, heightImage / block.y, 1);
	
	initLabels<<<grid, block>>>(Labels,widthImage,heightImage);
	printf("finished initialisation function \n");
	printf("finished scanning function \n");
	cudaThreadSynchronize();

		// check for error
		cudaError error2 = cudaGetLastError();
		if(error2 != cudaSuccess)
		{
			// print the CUDA error message and exit
			printf("CUDA error: %s\n", cudaGetErrorString(error2));
printf("error in initialisation function \n");
			exit(-1);
		}
}

//----------------------------------------
// host code for KERNEL 2
//----------------------------------------

void callScanning(unsigned int* Labels,int widthImage,int heightImage,bool* IsNotDone,int threadsX,int threadsY)
{
   printf("starting scanning function \n");
	dim3 block(threadsX, threadsY, 1);
	dim3 grid(widthImage / block.x, heightImage / block.y, 1);
	
	Scanning<<<grid, block>>>(Labels,widthImage,heightImage,IsNotDone);
	
	printf("finished scanning function \n");
	cudaThreadSynchronize();

		// check for error
		cudaError error2 = cudaGetLastError();
		if(error2 != cudaSuccess)
		{
			// print the CUDA error message and exit
			printf("CUDA error: %s\n", cudaGetErrorString(error2));
printf("error in scanning function \n");
			exit(-1);
		}
}

void callAnalysis(unsigned int* Labels,int widthImage,int heightImage,int threadsX,int threadsY)
{	printf("starting analysis function \n");
	dim3 block(threadsX, threadsY, 1);
	dim3 grid(widthImage / block.x, heightImage / block.y, 1);
	
	Analysis<<<grid, block>>>(Labels,widthImage,heightImage);
	printf("finished analysis function \n");
		cudaThreadSynchronize();

		// check for error
		cudaError error2 = cudaGetLastError();
		if(error2 != cudaSuccess)
		{
			// print the CUDA error message and exit
			printf("CUDA error: %s\n", cudaGetErrorString(error2));
printf("error in analysis function \n");
			exit(-1);
		}
  
}
void do_Reindexing(unsigned int* Labels,int widthImage,int heightImage,int threadsX,int threadsY);
void doCCL(unsigned int * Labels,int widthImage,int heightImage,int threadsX,int threadsY)
{
  callInitLabels(Labels,widthImage,heightImage,threadsX,threadsY);

  //using thrust vectors to avoid weird memcpys
  
  thrust::device_vector<bool> isNotDone(1);

  bool * raw_done;
isNotDone[0]=true;
  raw_done=thrust::raw_pointer_cast(&isNotDone[0]);
// int max_iter=1;
// int iter_num=1;

   while(isNotDone[0])
  {
  isNotDone[0]=false;
    callScanning(Labels,widthImage,heightImage,raw_done,threadsX,threadsY);
 
    callAnalysis(Labels,widthImage,heightImage,threadsX,threadsY);
//   return; 

//     MY_SAFE_CALL(cudaMemcpy(isNotDone,isNotDoneDevice,sizeof(bool),cudaMemcpyDeviceToHost));
// ++iter_num;   
}
do_Reindexing(Labels,widthImage,heightImage,threadsX,threadsY);

}

void callreIndex(unsigned int* Labels,int widthImage,int heightImage,unsigned int *input_array,unsigned int array_size,int threadsX,int threadsY)
{	printf("starting analysis function \n");
	dim3 block(threadsX, threadsY, 1);
	dim3 grid(widthImage / block.x, heightImage / block.y, 1);
	
	reIndex<<<grid, block>>>(Labels,widthImage,heightImage,input_array,array_size);
	printf("finished analysis function \n");
		cudaThreadSynchronize();

		// check for error
		cudaError error2 = cudaGetLastError();
		if(error2 != cudaSuccess)
		{
			// print the CUDA error message and exit
			printf("CUDA error: %s\n", cudaGetErrorString(error2));
printf("error in analysis function \n");
			exit(-1);
		}
  
}
template <typename T>
struct is_not_zero : public thrust::unary_function<T,bool>
{
    __host__ __device__
    bool operator()(T x)
    {
        return x !=0;
    }
};
/*

 
 template<typename T>
struct transform_functor : public thrust::unary_function<T,T>
{
    const unsigned int* vector_or;

    const unsigned int size_vector;


    transform_functor(unsigned int * _vector_or,unsigned int _vector_size) : vector_or(_vector_or),vector_size(_vector_size) {}

  __host__ __device__
        float operator()(const float& x) const { 
      unsigned int i;
      for (i=1;i<size_vector){
	if(x==vector_or[i])
	  break;
	  return i;
	}      
     }
};

*/

void do_Reindexing(unsigned int* Labels,int widthImage,int heightImage,int threadsX,int threadsY)
{	
  thrust::device_ptr<unsigned int> Labels_ptr(Labels);
  thrust::device_ptr<unsigned int> Labels_ptr_end=Labels_ptr+heightImage*widthImage;
  //create a new device vector to hold the result because unique modifies elements
  thrust::device_vector<unsigned int> new_labels(widthImage*heightImage);
  //first we need to sort the array
  thrust::copy(Labels_ptr,Labels_ptr_end,new_labels.begin());
  thrust::sort(new_labels.begin(),new_labels.end());
  //then we take the unique values in it
  thrust::device_vector<unsigned int>::iterator new_labels_end=thrust::unique(new_labels.begin(),new_labels.end());
 //we count the elements
//   int count=new_labels_end-new_labels.begin();
// since the values after values_end are garbage, we'll resize the vector
//if this line dissapears everything goes to HELL,there is an extra 7 that ruins everything
  new_labels.resize(new_labels_end - new_labels.begin());
// unsigned int count=count_if(new_labels.begin(),new_labels_end,is_not_zero<unsigned int>());
//  int count=new_labels.size();
//  printf("count %d \n",count);
int count2=new_labels.end()-new_labels.begin();
printf("count2 %d \n",count2);
  //transform the original labels
//  thrust::transform_if(Labels_ptr,Labels_ptr_end,transform_functor<unsigned int>(),is_not_zero());
unsigned int* raw_input=thrust::raw_pointer_cast(&new_labels[0]);
// return;
callreIndex(Labels,widthImage,heightImage,raw_input,count2,threadsX,threadsY);
}

